package org.checkboo.hopefullyengine.sceneelements;

import java.nio.FloatBuffer;
import java.nio.IntBuffer;

import org.checkboo.hopefullyengine.boundingvolumes.AxisAlignedBoundingBox;
import org.checkboo.hopefullyengine.boundingvolumes.BoundingVolume;
import org.checkboo.hopefullyengine.inversekinematics.Skeleton;
import org.checkboo.math.Vector2f;
import org.checkboo.math.Vector3f;
import org.lwjgl.BufferUtils;

public class BaseObject {
	
	private Vector3f[] vertices;
	private Vector3f[] displayVertices;
	private int[] indices;
	private Vector2f[] texCoords;
	private Vector3f[] triangleNormals;
	private Vector3f[] vertexNormals;
	private Vector3f[] displayVertexNormals;
	private int[] boneAssignements;
	private FloatBuffer normals;
	private IntBuffer indicesBuffer;
	private FloatBuffer verticesBuffer;
	private FloatBuffer texCoordsBuffer;
	private Skeleton skeleton;
	private Texture texture;
	                 
	private Color solidColor;
	
	public BaseObject(){
		this.vertices = new Vector3f[0];
		this.indices = new int[0];
	}
	
	public BaseObject(Vector3f[] vertices, int[] indices){		
		this.vertices = vertices;
		this.indices = indices;
		calculateTriangleNormals();
		calculateVertexNormals();
	}

	public void calculateTriangleNormals(){
		this.triangleNormals = new Vector3f[this.indices.length/3];
		Vector3f u = new Vector3f();
		Vector3f v = new Vector3f();
		for(int i=0; i<this.triangleNormals.length; i++){
			u.zero();
			v.zero();
			this.vertices[this.indices[i*3+1]].subtractRes(this.vertices[this.indices[i*3]], u);
			this.vertices[this.indices[i*3+2]].subtractRes(this.vertices[this.indices[i*3+1]], v);

			this.triangleNormals[i] = new Vector3f();
			v.crossRes(u, this.triangleNormals[i]);
			this.triangleNormals[i].normalize();
		}
	}
	
	public Vector3f getTriangleNormal(int index){
		return this.triangleNormals[index];
	}
	
	public Vector3f[] getTriangleNormals(){
		return this.triangleNormals;
	}
	
	public FloatBuffer getTriangleNormalBuffer(){
		FloatBuffer normals = BufferUtils.createFloatBuffer(this.triangleNormals.length*3);
		for(Vector3f vec : this.triangleNormals){
			if(null != vec)
				normals.put(vec.getX()).put(vec.getY()).put(vec.getZ());
		}
		normals.rewind();
		return normals;
	}
	
	public void setTriangleNormals(Vector3f[] normals){
		this.triangleNormals = normals;
	}
	
	public void calculateVertexNormals(){
		if(null == this.triangleNormals)
			calculateTriangleNormals();
		
		this.vertexNormals = new Vector3f[this.vertices.length];
		for(int i=0; i<triangleNormals.length; i++){
			for(int j=0; j<this.indices.length; j++){
				if(null == this.vertexNormals[this.indices[j]])
					this.vertexNormals[this.indices[j]] = new Vector3f();
				
				if(this.vertices[this.indices[j]].equals(this.vertices[this.indices[i*3+0]]))
					this.vertexNormals[this.indices[j]].add(this.triangleNormals[i]);
				if(this.vertices[this.indices[j]].equals(this.vertices[this.indices[i*3+1]]))
					this.vertexNormals[this.indices[j]].add(this.triangleNormals[i]);
				if(this.vertices[this.indices[j]].equals(this.vertices[this.indices[i*3+2]]))
					this.vertexNormals[this.indices[j]].add(this.triangleNormals[i]);
			}
		}
		for(Vector3f vec : this.vertexNormals)
			if(null != vec)
				vec.normalize();
	}
	
	public Vector3f getVertexNormal(int index){
		return this.vertexNormals[index];
	}
	
	public Vector3f[] getVertexNormals(){
		return this.vertexNormals;
	}
	
	public FloatBuffer getVertexNormalBuffer(){
		if(null == this.normals){
			normals = BufferUtils.createFloatBuffer(this.vertexNormals.length*3);
			for(Vector3f vec : this.vertexNormals){
				if(null != vec)
					normals.put(vec.getX()).put(vec.getY()).put(vec.getZ());
			}
			normals.rewind();
		}
		return normals;
	}
	
	public void setVertexNormals(Vector3f[] normals){
		this.displayVertexNormals = normals;
		this.vertexNormals = normals;
	}
	
	
	public Vector3f[] getVertices() {
		return vertices;
	}
	
	public FloatBuffer getVertexBuffer(){
		if(null == this.verticesBuffer)
			verticesBuffer = BufferUtils.createFloatBuffer(this.vertices.length*3);
		for(Vector3f vec : this.vertices){
			if(null != vec)
				verticesBuffer.put(vec.getX()).put(vec.getY()).put(vec.getZ());
		}
		verticesBuffer.rewind();
		return verticesBuffer;
	}

	public void setVertices(Vector3f[] vertices) {
		this.displayVertices = vertices;
		this.vertices = vertices;
	}

	public int[] getIndices() {
		return indices;
	}
	
	public IntBuffer getIndexBuffer(){
		if(null == this.indicesBuffer)
			indicesBuffer = BufferUtils.createIntBuffer(this.indices.length);
		indicesBuffer.put(this.indices).rewind();
		return indicesBuffer;
	}

	public void setIndices(int[] indices) {
		this.indices = indices;
	}

	public Color getSolidColor() {
		return solidColor;
	}

	public void setSolidColor(Color solidColor) {
		this.solidColor = solidColor;
	}

	public Vector2f[] getTexCoords() {
		return texCoords;
	}
	
	public FloatBuffer getTexCoordsBuffer(){
		if(null == this.texCoordsBuffer)
			this.texCoordsBuffer = BufferUtils.createFloatBuffer(this.texCoords.length*2);
		this.texCoordsBuffer.rewind();
		for(Vector2f vec : this.texCoords){
			if(null != vec)
				this.texCoordsBuffer.put(vec.getX()).put(vec.getY());
		}
		this.texCoordsBuffer.rewind();
		return this.texCoordsBuffer;
	}

	public void setTexCoords(Vector2f[] texCoords) {
		this.texCoords = texCoords;
	}

	public int[] getBoneAssignements() {
		return boneAssignements;
	}

	public void setBoneAssignements(int[] boneAssignements) {
		this.boneAssignements = boneAssignements;
	}

	public Skeleton getSkeleton() {
		return skeleton;
	}

	public void setSkeleton(Skeleton skeleton) {
		this.skeleton = skeleton;
	}
	
	public Vector3f[] getDisplayVertices() {
		return displayVertices;
	}
	
	public FloatBuffer getDisplayVerticesBuffer(){
		FloatBuffer displayBuffer = BufferUtils.createFloatBuffer(this.vertices.length*3);
		for(Vector3f vec : this.displayVertices){
			if(null != vec)
				displayBuffer.put(vec.getX()).put(vec.getY()).put(vec.getZ());
		}
		displayBuffer.rewind();
		return displayBuffer;
	}

	public void setDisplayVertices(Vector3f[] displayVertices) {
		this.displayVertices = displayVertices;
	}

	public Vector3f[] getDisplayVertexNormals() {
		return displayVertexNormals;
	}
	
	public FloatBuffer getDisplayVertexNormalsBuffer(){
		FloatBuffer displayBuffer = BufferUtils.createFloatBuffer(this.vertices.length*3);
		for(Vector3f vec : this.displayVertexNormals){
			if(null != vec)
				displayBuffer.put(vec.getX()).put(vec.getY()).put(vec.getZ());
		}
		displayBuffer.rewind();
		return displayBuffer;
	}

	public void setDisplayVertexNormals(Vector3f[] displayVertexNormals) {
		this.displayVertexNormals = displayVertexNormals;
	}

	public void reset(){
		for(int i=0; i<this.displayVertices.length; i++){
			this.vertices[i].set(this.displayVertices[i]);
			this.vertexNormals[i].set(this.displayVertexNormals[i]);
		}
		
	}

	public Texture getTexture() {
		return texture;
	}

	public void setTexture(Texture texture) {
		this.texture = texture;
	}
	
	public int getNumVertices(){
		return this.vertices.length;
	}
}
